//
// Created by v on 19-7-9.
//

#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#include "CMCoapBroadcast.h"

#define CPLOG(d, fmt, ...)                                                     \
    do                                                                         \
    {                                                                          \
        if (d)                                                                 \
        {                                                                      \
            printf("[%s:%d] ", __func__, __LINE__);                            \
            printf(fmt, ##__VA_ARGS__);                                        \
        }                                                                      \
    } while (0)

CMCoapBroadcast::CMCoapBroadcast(const char *addr, int port)
{
    m_broadAddr = addr;
    m_broadPort = port;
    m_debug     = false;
    m_broadSock = 0;
    m_recvData  = "";
    int ret     = initSock();
    CPLOG(m_debug, "ini sock result [%d]\n", ret);
}

CMCoapBroadcast::~CMCoapBroadcast()
{
    if (m_broadSock > 0)
    {
        close(m_broadSock);
    }
}

int CMCoapBroadcast::initSock()
{
    m_broadSock = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in addrto;
    memset(&addrto, 0, sizeof(addrto));
    addrto.sin_family      = AF_INET;
    addrto.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    addrto.sin_addr.s_addr = inet_addr(m_broadAddr.c_str());
    //    inet_pton(AF_INET, m_broadAddr.c_str(), &addrto.sin_addr);
    addrto.sin_port = htons(m_broadPort);

    memcpy(&m_addrtoBroad, &addrto, sizeof(addrto));

    const int opt = 1;
    int nb = setsockopt(m_broadSock, SOL_SOCKET, SO_BROADCAST, (char *)&opt,
                        sizeof(opt));
    if (nb == -1)
    {
        CPLOG(m_debug, "set broadcast fail [%d:%s]\n", errno,
              gai_strerror(errno));
        if (m_broadSock > 0)
        {
            close(m_broadSock);
        }
        m_broadSock = 0;
        return -3;
    }

    CPLOG(m_debug, "broadcast set success\n");
    return m_broadSock;
}

int CMCoapBroadcast::sendData(const char *data, size_t len, const char *opt,
                              int id)
{
    if (m_broadSock <= 0)
        return -1;
    int ret = -1;
#ifdef WITH_COAP
    m_sendData = data;

    coap_pdu_t *pdu = coap_pdu_init(COAP_MESSAGE_CON, 2, id, 128);

    coap_add_option(pdu, COAP_OPTION_URI_PATH, strlen(opt),
                    (const uint8_t *)opt);

    coap_add_data(pdu, len, (const uint8_t *)data);
    coap_pdu_encode_header(pdu, COAP_PROTO_UDP);

    ret = sendto(m_broadSock, pdu->token - pdu->hdr_size,
                 pdu->hdr_size + pdu->used_size, 0,
                 (struct sockaddr *)&m_addrtoBroad, sizeof(struct sockaddr));
    if (ret == -1)
    {
        CPLOG(m_debug, "send out error [%d:%s]\n", errno, gai_strerror(errno));
    }
    coap_delete_pdu(pdu);
#endif

    return ret;
}

int CMCoapBroadcast::recvData(char **data, size_t *len, int *id)
{
    if (m_broadSock <= 0)
        return -1;
    int ret = 0;
#ifdef WITH_COAP
    socklen_t socklen      = sizeof(struct sockaddr);
    char      rdata[10240] = {0};
    size_t    rlen         = 10240;
    ret                    = recvfrom(m_broadSock, rdata, rlen, MSG_DONTWAIT,
                   (struct sockaddr *)&m_addrtoBroad, &socklen);
    if (ret == -1)
    {
        CPLOG(m_debug, "recv error [%d:%s]\n", errno, gai_strerror(errno));
        return -1111;
    }
    else
    {
        rlen = ret;
    }

    CPLOG(m_debug, "recv full data len [%d][%s]\n", rlen, rdata + 4);
    coap_pdu_t *pdu = coap_pdu_init(COAP_MESSAGE_NON, 2, 0, 128);
    ret = coap_pdu_parse(COAP_PROTO_UDP, (const uint8_t *)rdata, rlen, pdu);
    if (ret < 0)
    {
        CPLOG(m_debug, "parse error [%d]\n", ret);
        return ret;
    }
    CPLOG(m_debug, "max %d, alloc %u, token %c, used %u, head %d\n",
          pdu->max_hdr_size, pdu->alloc_size, pdu->token_length, pdu->used_size,
          pdu->hdr_size);
    uint8_t *rrdata = NULL;
    size_t   rrlen  = 0;
    ret             = coap_get_data(pdu, &rrlen, &rrdata);
    if (ret <= 0)
    {
        CPLOG(m_debug, "get data failed rrlen [%u]\n", rrlen);
        if (rrlen == 0)
        {
            return -5;
        }
    }

    *len         = rrlen;
    rrdata[*len] = '\0';
    CPLOG(m_debug, "get data tid [%d] [%u][%s]\n", pdu->tid, rrlen, rrdata);

    m_recvData = (char *)rrdata;
    //    memcpy(data, rrdata, *len);
    if (id)
        *id = pdu->tid;

    coap_delete_pdu(pdu);

    if (data)
        *data = (char *)m_recvData.c_str();
#endif
    return ret;
}
